package Leetcode.数组字符串;

import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;
import java.util.TreeMap;

/**
 * @Author: kirito
 * @Date: 2024/3/14 18:16
 * @Description:
给你一个整数数组 nums ，数组中共有 n 个整数。132 模式的子序列 由三个整数 nums[i]、nums[j] 和 nums[k] 组成，并同时满足：i < j < k 和 nums[i] < nums[k] < nums[j] 。

如果 nums 中存在 132 模式的子序列 ，返回 true ；否则，返回 false 。



示例 1：

输入：nums = [1,2,3,4]
输出：false
解释：序列中不存在 132 模式的子序列。
示例 2：

输入：nums = [3,1,4,2]
输出：true
解释：序列中有 1 个 132 模式的子序列： [1, 4, 2] 。
示例 3：

输入：nums = [-1,3,2,0]
输出：true
解释：序列中有 3 个 132 模式的的子序列：[-1, 3, 2]、[-1, 3, 0] 和 [-1, 2, 0] 。

n == nums.length
1 <= n <= 2 * 105
-109 <= nums[i] <= 109
 */

public class find132pattern {
    public boolean find132pattern(int[] nums) {
        int n = nums.length;
        if (n < 3) {
            return false;
        }

        // 左侧最小值
        int leftMin = nums[0];
        // 右侧所有元素
        TreeMap<Integer, Integer> rightAll = new TreeMap<Integer, Integer>();

        // 统计右侧所有元素的频率，从索引2开始
        for (int k = 2; k < n; ++k) {
            rightAll.put(nums[k], rightAll.getOrDefault(nums[k], 0) + 1);
        }

        // 从索引1到n-2遍历数组
        for (int j = 1; j < n - 1; ++j) {
            // 检查当前元素是否大于左侧最小值
            if (leftMin < nums[j]) {
                // 寻找大于左侧最小值+1的下一个最小元素
                /*
                 * TreeMap 是 Java 中的一种有序映射结构，它根据键的自然顺序进行排序。
                 * 在这种情况下，我们使用 TreeMap 来存储右侧的所有元素，并根据元素的值进行排序。
                 * 这样，我们可以使用 TreeMap 的一些有用方法来查找比给定值大的最小元素。
                 *
                 * 具体来说，我们使用 TreeMap 的 ceilingKey() 方法来寻找大于给定值的最小键。
                 * 这样，当我们检查当前元素是否大于左侧最小值时，我们可以使用 ceilingKey()
                 * 方法找到右侧大于左侧最小值+1的最小元素。如果找到了这样的元素且它小于当前元素，那么就存在132模式。
                 *
                 * 通过使用 TreeMap，我们可以在 O(log n) 的时间复杂度内进行插入、查找和删除操作，这使得算法的整体性能比较高效。
                 */
                //查看j位置的右边是否存在比他小的数
                Integer next = rightAll.ceilingKey(leftMin + 1);
                // 如果存在这样的元素且它小于当前元素，则返回true
                if (next != null && next < nums[j]) {
                    return true;
                }
            }
            // 如果当前元素比左侧最小值小，则更新左侧最小值
            leftMin = Math.min(leftMin, nums[j]);
            // 减少右侧下一个元素在rightAll中的频率
            rightAll.put(nums[j + 1], rightAll.get(nums[j + 1]) - 1);
            // 如果右侧下一个元素的频率为0，则将其从rightAll中删除
            if (rightAll.get(nums[j + 1]) == 0) {
                rightAll.remove(nums[j + 1]);
            }
        }

        // 未找到132模式
        return false;
    }

    /**
     * 假设我们有以下示例数组：nums = [3, 1, 4, 2].
     *
     * 我们从最后一个元素开始，即 nums[3] = 2，将其作为初始的候选 k 值，并将 maxK 初始化为负无穷大。
     *
     * candidateK 双端队列: [2]
     * maxK: -∞
     * 继续向前遍历数组：
     *
     * 处理 nums[2] = 4：
     *
     * 由于 4 大于候选的 k 值 2，我们更新 maxK 为 4。
     * candidateK 双端队列: [2]
     * maxK: 4
     * 处理 nums[1] = 1：
     *
     * 1 小于 maxK（即 1 < 4），说明找到了132模式，返回 true。
     * 在这个示例中，我们找到了132模式，因为存在一个递减的 k 值序列 [2, 4]，并且存在一个小于 4 的元素 1，满足132模式的条件。
     *
     * 总结一下算法的思路：
     *
     * 我们从最后一个元素开始，将其作为初始的候选 k 值，并将 maxK 初始化为负无穷大。
     * 从数组的倒数第二个元素开始向前遍历。
     * 对于每个元素：
     * 如果当前元素小于 maxK，说明找到了132模式，返回 true。
     * 如果当前元素大于候选的 k 值，更新 maxK 为当前元素。
     * 如果当前元素大于 maxK，将其作为新的候选 k 值。
     * 如果遍历完整个数组仍然没有找到132模式，返回 false。
     * @param nums
     * @return
     */
    public boolean find132pattern2(int[] nums) {
        int n = nums.length;
        Deque<Integer> candidateK = new LinkedList<Integer>();
        candidateK.push(nums[n - 1]);
        int maxK = Integer.MIN_VALUE;

        for (int i = n - 2; i >= 0; --i) {
            // 如果当前元素比最大的k值小，说明存在132模式
            if (nums[i] < maxK) {
                return true;
            }
            // 将当前元素与候选的k值进行比较，更新最大的k值
            while (!candidateK.isEmpty() && nums[i] > candidateK.peek()) {
                maxK = candidateK.pop();
            }
            // 如果当前元素大于最大的k值，将其作为候选的k值
            if (nums[i] > maxK) {
                candidateK.push(nums[i]);
            }
        }

        return false;
    }

    /**
     * 慧策一轮笔试 +LRU   LinkHshMap
     * @param nums
     * @return
     *
     */
    public static int count132pattern(int[] nums) {
        int n = nums.length, res = 1000, mn = 1000;
        int[] left = new int[n];
        //初始化中间数组，第i个代表右边最小的值是多少
        for (int i = 1; i < n; i++) {
            left[i] = mn = Math.min(nums[i - 1], mn);
        }

        int right = nums[n - 1];
        for (int i = n - 2; i > 0; i--) {
            if (left[i] < nums[i] && nums[i] > right) {
                res = Math.min(res, left[i] + nums[i] + right);
            }
            right = Math.min(right, nums[i]);
        }

        return res < 1000 ? res : -1;
    }

    public static void main(String[] args) {
        int[] a = {1,3,2,1,2,1};
        System.out.println(count132pattern(a));
    }

}
